package com.rjs.nio.channel;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * Selector支持单线程处理多个Channel
 * 详细的文档：http://blog.csdn.net/dd864140130/article/details/50299687
 * @author LiuZhenghua
 * 2017年12月15日 下午6:02:37
 */
public class SelectorDemo {

	public static void main(String[] args) throws IOException {
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.socket().bind(new InetSocketAddress(9999));
		//设置非阻塞模式
		serverSocketChannel.configureBlocking(false);
		
		/**
		 * 1.注册selector的通道必须为非阻塞模式
		 * 2.感兴趣集合有4种
		 * SelectionKey.OP_ACCEPT 一个ServerSocketChannel准备好接收新进入的连接（接收就绪）
		 * SelectionKey.OP_CONNECT 一个SocketChannel已经连接完成了
		 * SelectionKey.OP_READ 一个通道已经读完了（读就绪）
		 * SelectionKey.OP_WRITE 等待写数据的通道（写就绪）
		 * 3.SelectorChannel.validOps()可以获取当前Channel支持的集合
		 */
		Selector selector = Selector.open();
		SelectionKey selectionKey = serverSocketChannel.register(selector, serverSocketChannel.validOps());
		//可以附加对象
		selectionKey.attach("hello world");
		
		ByteBuffer buf = ByteBuffer.allocate(3);
		byte[] b = new byte[128];
		int len = -1;
		while (true) {
			/**
			 * Selector维护三种类型的键值集合
			 * 1.selector.keys()返回已注册的keys
			 * 2.selector.selectedKeys() 已准备好的，并且包含interests的键集合
			 * 3.cancel()方法被调用过的键(这个键已经被无效化)，但它们还没有被注销。这个集合是选择器对象的私有成员，因而无法直接访问。
			 */
			if (selector.select() == 0) {
				continue;
			}
			Set<SelectionKey> selectedKeys = selector.selectedKeys();
			for (Iterator<SelectionKey> iterator = selectedKeys.iterator(); iterator.hasNext();) {
				SelectionKey key = (SelectionKey) iterator.next();
				if (key.isAcceptable()) {
					ServerSocketChannel server = (ServerSocketChannel)key.channel();
					SocketChannel socketChannel = server.accept();
					socketChannel.configureBlocking(false);
					socketChannel.register(selector, SelectionKey.OP_READ);
				} else if (key.isReadable()) {
					SocketChannel socketChannel = (SocketChannel)key.channel();
					while((len = socketChannel.read(buf)) != -1){
						buf.flip();
						buf.get(b, 0, len);
						System.out.println(new String (b, 0, len));
						//或者一个个的读取
						/*while(buf.hasRemaining()) {
							System.out.println((char)buf.get());
						}*/
						buf.clear();
					}
				}
				iterator.remove();
			}
		}
		
	}
}
